package com.zh.core;

/**
 * Created by zhaohui on 2019/11/14
 */
public class WaysToExchangeMoney {

    public static int process1(int[] arr, int index, int aim) {
        int res = 0;
        if (index == arr.length) {
            res = aim == 0 ? 1 : 0;
        } else {
            for (int i = 0; i * arr[index] <= aim; i++) {
                res += process1(arr, index + 1, aim - i * arr[index]);
            }
        }
        return res;
    }

    public static int coins1(int[] arr, int aim) {
        if (arr == null || arr.length == 0 || aim < 0) {
            return 0;
        }
        return process1(arr, 0, aim);
    }

    public static int process2(int[] arr, int index, int aim, int[][] map) {
        int res = 0;
        if (index == arr.length) {
            res = aim == 0 ? 1 : 0;
        } else {
            int mapValue;
            for (int i = 0; i * arr[index] <= aim; i++) {
                mapValue = map[index + 1][aim - i * arr[index]];
                if (mapValue != 0) {
                    res += mapValue == -1 ? 0 : mapValue;
                } else {
                    res += process2(arr, index + 1, aim - i * arr[index], map);
                }
            }
        }
        map[index][aim] = res == 0 ? -1 : res;
        return res;
    }

    public static int coins2(int[] arr, int aim) {
        if (arr == null || arr.length == 0 || aim < 0) {
            return 0;
        }
        int[][] map = new int[arr.length + 1][aim + 1];
        return process2(arr, 0, aim, map);
    }

    public static int coins3(int[] arr, int aim) {
        if (arr == null || arr.length == 0 || aim < 0) {
            return 0;
        }
        int row = arr.length;
        int col = aim + 1;
        int[][] dp = new int[row][col];
//        for (int j = 1; j < col; j++) {
//            if (j % arr[0] == 0) {
//                dp[0][j] = 1;
//            }
//        }
        for (int i = 0; i < row; i++) {
            dp[i][0] = 1;
        }
        for (int j = 1; arr[0] * j < col; j++) {
            dp[0][arr[0]*j] = 1;
        }
        for (int i = 1; i < row; i++) {
            for (int j = 1; j < col; j++) {
                for (int k = 0; j - k * arr[i] >= 0; k++) {
                    dp[i][j] += dp[i-1][j-k*arr[i]];
                }
            }
        }
        return dp[row-1][col-1];
    }

    public static int coins4(int[] arr, int aim) {
        if (arr == null || arr.length == 0 || aim < 0) {
            return 0;
        }
        int row = arr.length;
        int col = aim + 1;
        int[][] dp = new int[row][col];
        for (int i = 0; i < row; i++) {
            dp[i][0] = 1;
        }
        for (int j = 1; j * arr[0] < col; j++) {
            dp[0][j * arr[0]] = 1;
        }
        for (int i = 1; i < row; i++) {
            for (int j = 1; j < col; j++) {
                dp[i][j] = dp[i - 1][j];
                dp[i][j] += j - arr[i] >= 0 ? dp[i][j - arr[i]] : 0;
            }
        }
        return dp[row - 1][col - 1];
    }

    public static int coins5(int[] arr, int aim) {
        if (arr == null || arr.length == 0 || aim < 0) {
            return 0;
        }
        int len = aim + 1;
        int[] dp = new int[len];
        dp[0] = 1;
        for (int j = 1; j * arr[0] <= aim; j++) {
            dp[j * arr[0]] = 1;
        }
        for (int i = 1; i < arr.length; i++) {
            for (int j = 1; j <= aim; j++) {
                dp[j] += j - arr[i] >= 0 ? dp[j - arr[i]] : 0;
            }
        }
        return dp[aim];
    }

    public static void main(String[] args) {
        int[] arr = {5, 10, 25, 1};
        int aim = 1000;
        System.out.println(coins1(arr, aim));
        System.out.println(coins2(arr, aim));
        System.out.println(coins3(arr, aim));
        System.out.println(coins4(arr, aim));
        System.out.println(coins5(arr, aim));
    }

}
